Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Implementation of Switch-Case Statement in TeX

391 views
Skip to first unread message

Martin Scharrer

unread,
Apr 27, 2009, 5:41:17 PM4/27/09
to
Hi,

I recently had the idea about a switch case statement for use in LaTeX
packages. I needed to check user input (a string) and execute
different macros on different inputs. of a list. Because I could not
find a switch-case statement I implemented it using code like this:
\def\@tempa{foo}
\ifx\@tempa\user@input
...
\else
\def\@tempa{bar}
\ifx\@tempa\user@input
...
\fi\fi

Now I found the time to code a switch-case statement in plainTeX:

My idea about the syntax is:

\switch{<input value>}
\case{<case 1 value>}{<case 1 code}
\case{<case 2 value>}{<case 2 code}
...
\case{<case n value>}{<case n code}
\default{<default code>}
\endswitch

Instead of \endswitch there could also be \hctiws (switch reversed
like \fi for \if).
I also implemented versions of \switch and \case which expand there
argument: \eswitch, \ecase ('e' like 'edef'), or
check the definition instead the content: \switchx, \casex ('x' like
'ifx'). The different \switch and \case macros can be mixed.

The switch-statement stores the code in a macro similar to macros like
\@ifnextchar and uses a TeX group to localise its internal macros. The
code of the selected switch is then executed after the end of the
group. This makes the switch cascade-able and allows the case code to
include macros which read the tokens after the \endswitch.

Here my code. It is not yet fully tested. Comments are very welcome. I
might provide this as a package. Any suggestion about a name? Maybe
'switch' or 'swcase'. 'switchcase' might be too long.

%%%%%%%%%%% switch.tex %%%%%%%%%%%%%
\catcode`\@=\catcode`\A

% Normal (no expansion):

\def\switch#1{%
\begingroup
\let\switch@exec\empty
\def\switch@want{#1}%
\ignorespaces
}

\def\case#1#2{%
\def\switch@might{#1}%
\ifx\switch@might\switch@want
\def\switch@exec{#2}%
\expandafter\switch@skiprest
\fi
\ignorespaces
}

% Expansion of the argument ('e' as in 'edef'):

\def\eswitch#1{%
\begingroup
\let\switch@exec\empty
\edef\switch@want{#1}%
\ignorespaces
}

\def\ecase#1#2{%
\edef\switch@might{#1}%
\ifx\switch@might\switch@want
\def\switch@exec{#2}%
\expandafter\switch@skiprest
\fi
\ignorespaces
}

% Check definition not content ('x' as in 'ifx'):

\def\switchx#1{%
\begingroup
\let\switch@exec\empty
\let\switch@want#1\relax
\ignorespaces
}

\def\casex#1#2{%
\ifx\switch@want#1\relax
\def\switch@exec{#2}%
\expandafter\switch@skiprest
\fi
\ignorespaces
}

% Default case. Identical for all different switch statements.

\def\default#1{%
\def\switch@exec{#1}%
\switch@skiprest
}

\def\switch@skiprest#1\endswitch{%
\expandafter\switch@realend
\expandafter{\switch@exec}%
}

\def\switch@realend#1{%
\endgroup
#1%
}

\def\endswitch{%
}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% Some Testcases:
\newlinechar=`^^J

\def\test#1{%
\switch{#1}
\case{test1}{\message{test1^^J}}
\case{test2}{\message{test2^^J}}
\case{test3}{\message{test3^^J}}
\case{test4}{\message{sub-switch 1:^^J }
\switch{abc}
\case{a}{\message{a^^J}}
\case{ab}{\message{ab^^J}}
\case{abc}{\message{abc^^J}}
\endswitch
}
\case{test5}{\message{sub-switch 2:^^J }\test{test1}}
\default{\message{default^^J}}
\endswitch
}

\message{^^J switch: ^^J}
\test{test1}
\test{test4}
\test{other}
\test{test5}

\def\testveca{test1}
\def\testvecb{test2}
\def\testvecc{test3}
\def\testvecd{test4}
\let\testwant\testvecb

\message{^^J switchx: ^^J}
\switchx{\testwant}
\casex{\testveca}{\message{testa^^J}}
\casex{\testvecb}{\message{testb^^J}}
\casex{\testvecc}{\message{testc^^J}}
\casex{\testvecd}{\message{testd^^J}}
\default{\message{default2^^J}}
\endswitch


\def\testwant{\testvecc}

\message{^^J eswitch: ^^J}
\eswitch{\testwant}
\ecase{\testveca}{\message{testa^^J}}
\ecase{\testvecb}{\message{testb^^J}}
\ecase{\testvecc}{\message{testc^^J}}
\ecase{\testvecd}{\message{testd^^J}}
\default{\message{default2^^J}}
\endswitch

% End TeX of LaTeX run
\csname bye\endcsname
\end{document}

%%%%%%%%%%% END OF CODE %%%%%%%%%%%%

Dan Luecking

unread,
Apr 27, 2009, 6:22:43 PM4/27/09
to

Some observations:

The different switch commands are necessary, but the
user need not have to remember different \case commands:

\def\initswitch{% shorthand
\begingroup
\let\switch@exec\empty
}
\def\switch#1{%
\initswitch
\def\switch@want{#2}% or \edef for \eswitch
\let\case\normalcase% or \expandingcase
\ignorespaces
}
\def\switchx#1{
\initswitch
\let\switch@want#2%
\let\case\meaningcase
\ignorespaces
}

Except for the first line, all the case commands are
nearly the same, so code could be shared.

\def\normalcase#1{%
\def\switch@might{#1}% \edef for \expandingcase
\fincase
}
\def\meaningcase#1{%
\let\switch@might#1%
\fincase
}
\long\def\fincase#1{% \long in case of \par
\ifx\switch@might\switch@want
\def\switch@exec{#1}%
\expandafter\switch@skiprest
\fi
\ignorespaces
}

>\def\switch@skiprest#1\endswitch{%


> \expandafter\switch@realend
> \expandafter{\switch@exec}%
>}
>
>\def\switch@realend#1{%
> \endgroup
> #1%
>}

Or
\def\switch@skiprest#1\endswitch{%
\expandafter\endgroup\switch@exec
}


Dan
To reply by email, change LookInSig to luecking

Martin Scharrer

unread,
Apr 27, 2009, 6:33:19 PM4/27/09
to
I had this idea too, but I decided that in some applications different
switch and case statements need to be mixed, e.g. a \ecase inside
\switch if the case value is given by a expression which must first be
expanded. Also a non-expanding \case inside a \eswitch where only the
user input muse be expanded.
This way the whole thing is much more flexible.

> Or
> \def\switch@skiprest#1\endswitch{%
>   \expandafter\endgroup\switch@exec
> }

Thanks for that suggestion. I just learned something.

Best,
Martin

Donald Arseneau

unread,
Apr 28, 2009, 5:01:23 AM4/28/09
to
On Apr 27, 2:41 pm, Martin Scharrer <mar...@scharrer-online.de> wrote:
> I recently had the idea about a switch case statement for use in LaTeX
> packages. I needed to check user input (a string) and execute
> different macros on different inputs.

In general, this is done with \csname

\csname IDENT:<sanitized_user_text>\endcsname

where the IDENT string is different from regular macros and
for different switch uses.

You would define

\@namedef{IDENT:<case 1 text>}{<case 1 code>}
\@namedef{IDENT:<case 2 text>}{<case 2 code>}

Your macros look good though..

Donald Arseneau as...@triumf.ca

Martin Scharrer

unread,
Apr 28, 2009, 5:27:37 AM4/28/09
to
On Apr 28, 10:01 am, Donald Arseneau <a...@triumf.ca> wrote:
> On Apr 27, 2:41 pm, Martin Scharrer <mar...@scharrer-online.de> wrote:
>
> > I recently had the idea about a switch case statement for use in LaTeX
> > packages. I needed to check user input (a string) and execute
> > different macros on different inputs.
>
> In general, this is done with \csname
>
>  \csname IDENT:<sanitized_user_text>\endcsname
>
> where the IDENT string is different from regular macros and
> for different switch uses.
>
> You would define
>
> \@namedef{IDENT:<case 1 text>}{<case 1 code>}
> \@namedef{IDENT:<case 2 text>}{<case 2 code>}
Thanks, I knew and use this in some packages, too. E.g. a lot in my
new package 'tikz-timing' where the user input causes a diagram to be
drawn.
However, in cases where the user input must be checked in different
places (with different case code) this method becomes, IMHO, a little
extensive, because a lot of macros must be defined and cascaded
switches are not easily possible. However, this solution with \@nameuse
{IDENT:<sanit. user input> should be expandable, while my switch
statement is not.

> Your macros look good though..

Thanks.

Martin


Jonathan Fine

unread,
Apr 28, 2009, 5:36:05 AM4/28/09
to
Martin Scharrer wrote:
> Hi,
>
> I recently had the idea about a switch case statement for use in LaTeX
> packages. I needed to check user input (a string) and execute
> different macros on different inputs. of a list. Because I could not
> find a switch-case statement I implemented it using code like this:
> \def\@tempa{foo}
> \ifx\@tempa\user@input
> ...
> \else
> \def\@tempa{bar}
> \ifx\@tempa\user@input
> ...
> \fi\fi


I wrote an article about basic control sequences (including \switch and
\CASE) in the 1990s.
http://www.tug.org/TUGboat/Articles/tb13-1/tb34fine.pdf
http://www.tug.org/TUGboat/Articles/tb14-1/tb38fine.pdf

Please take a look at them.

--
Jonathan

Martin Scharrer

unread,
Apr 28, 2009, 5:46:55 AM4/28/09
to
Thank you so much for the links. I will have a deeper look at them on
the weekend.

I just saw a package 'control.sty' mentioned in one of this
publications, but I can't find it on CTAN.

Thanks,
Martin


Jonathan Fine

unread,
Apr 28, 2009, 6:29:20 AM4/28/09
to
Martin Scharrer wrote:

>> I wrote an article about basic control sequences (including \switch and
>> \CASE) in the 1990s.
>> http://www.tug.org/TUGboat/Articles/tb13-1/tb34fine.pdf
>> http://www.tug.org/TUGboat/Articles/tb14-1/tb38fine.pdf
>>
>> Please take a look at them.
> Thank you so much for the links. I will have a deeper look at them on
> the weekend.
>
> I just saw a package 'control.sty' mentioned in one of this
> publications, but I can't find it on CTAN.

Apologies. I don't think it ever got to onto CTAN. I've probably got a
copy at home, somewhere.

There's nothing in that file that's not in the articles.

--
Jonathan

Donald Arseneau

unread,
Apr 28, 2009, 4:14:06 PM4/28/09
to
On Apr 28, 2:27 am, Martin Scharrer <mar...@scharrer-online.de> wrote:
> On Apr 28, 10:01 am, Donald Arseneau <a...@triumf.ca> wrote:
> > \csname IDENT:<sanitized_user_text>\endcsname

> {IDENT:<sanit. user input> should be expandable, while my switch
> statement is not.

It depends on how much sanitization is needed, which can be a problem.


I forget already, is \scantokens expandable? If so, it would be much
better for this than the old \def...\meaning.

Donald Arseneau

Martin Scharrer

unread,
Apr 29, 2009, 6:39:49 AM4/29/09
to
Are you thinking to use \scantokens to verbatim'ise the user input? In
this case
you need \catcode calls which are assignments (unexpandable) or aren't
they?

You don't need sanitization in all cases. \csname expands the macros
which allows the user to provide it's input as macro which is normally
what you want.
If a unexpandable macro is provided \csname will fail (at least I
think so) and the user will become the appropriate error message. Then
it is his/her problem.

Martin

Donald Arseneau

unread,
Apr 30, 2009, 8:34:41 PM4/30/09
to
On Apr 29, 3:39 am, Martin Scharrer <mar...@scharrer-online.de> wrote:
> On Apr 28, 9:14 pm, Donald Arseneau <a...@triumf.ca> wrote:> On Apr 28, 2:27 am, Martin Scharrer <mar...@scharrer-online.de> wrote:
>
> > > On Apr 28, 10:01 am, Donald Arseneau <a...@triumf.ca> wrote:
> > > > \csname IDENT:<sanitized_user_text>\endcsname
> > > {IDENT:<sanit. user input> should be expandable, while my switch
> > > statement is not.
>
> > It depends on how much sanitization is needed, which can be a problem.
>
> > I forget already, is \scantokens expandable? If so, it would be much
> > better for this than the old \def...\meaning.
>
> Are you thinking to use \scantokens to verbatim'ise the user input? In
> this case
> you need \catcode calls which are assignments (unexpandable) or aren't
> they?

Ah right, so still no better than \meaning.

> If a unexpandable macro is provided \csname will fail (at least I
> think so) and the user will become the appropriate error message. Then
> it is his/her problem.

Yes, it was sanitization of such things that I was thinking about.
Your code using \def and \ifx works fine (I expect) with such
strings whereas unsanitized \csname will fail.

Donald

Morten Høgholm

unread,
May 1, 2009, 4:10:54 PM5/1/09
to
On Mon, 27 Apr 2009 23:41:17 +0200, Martin Scharrer wrote:

> Hi,
>
> I recently had the idea about a switch case statement for use in LaTeX
> packages. I needed to check user input (a string) and execute
> different macros on different inputs. of a list. Because I could not
> find a switch-case statement I implemented it using code like this:
> \def\@tempa{foo}
> \ifx\@tempa\user@input
> ...
> \else
> \def\@tempa{bar}
> \ifx\@tempa\user@input
> ...
> \fi\fi
>
> Now I found the time to code a switch-case statement in plainTeX:
>
> My idea about the syntax is:
>
> \switch{<input value>}
> \case{<case 1 value>}{<case 1 code}
> \case{<case 2 value>}{<case 2 code}
> ...
> \case{<case n value>}{<case n code}
> \default{<default code>}
> \endswitch

I the expl3 code we have case switches that look similar to this. From the
documentation (in l3prg.dtx) for the function called \prg_case_int:nnn we
have

% This function evaluates the first \meta{integer expr} and then compares
it
% to the values found in the list. Thus the expression
% \begin{verbatim}
% \prg_case_int:nnn{2*5}{
% {5}{Small} {4+6}{Medium} {-2*10}{Negative}
% }{Other}
% \end{verbatim}
% evaluates first the term to look for and then tries to find this
% value in the list of values. If the value is found, the code on its
% right is executed after removing the remainder of the list. If the
% value is not found, the \meta{else case} is executed. The example
% above will return ``Medium''.

Others exist and the code is fairly simple. You might be able to find
inspiration in the implementation or come up with suggestions for
improvements.


--
Morten

0 new messages